/**********************************************************
 * Copyright © 2024 Walkline Wang (https://walkline.wang)
 * Gitee: https://gitee.com/walkline/keypad-for-ops
 **********************************************************/
#include "ws2812.h"

SPIClass SPI_2(LEDS_MOSI, LEDS_MISO, LEDS_SCLK);

SPI_HandleTypeDef spi2_handle = *SPI_2.getHandle();
DMA_HandleTypeDef hdma_tx;

void WS2812::setup() {
    SPI_2.begin();
    SPI_2.setBitOrder(MSBFIRST);
    SPI_2.setDataMode(SPI_MODE1);
    SPI_2.setClockDivider(SPI_CLOCK_DIV2);

    init_spi_with_dma();

    memset(dma_buffer, 0, DMA_BUFFER_SIZE);

    clean();
}

void WS2812::brightness(float value) {
    bright_percent = constrain(value, 0.0f, 1.0f);
}

void WS2812::set_color(int index, ws2812_color_t color) {
    uint8_t gap_buffer[WS2812_RESET_PULSE] = {};

    color.brightness(bright_percent);

    if (index <= LEDS_ALL) {
        uint8_t *ptr = dma_buffer;

        for (uint8_t count = 0; count < LEDS_COUNT; count++) {
            FILL_DMA_BUFFER(color.green);
            FILL_DMA_BUFFER(color.red);
            FILL_DMA_BUFFER(color.blue);
        }

        spi_transmit_dma();
    } else if (index < LEDS_COUNT) {
        uint8_t *ptr = &dma_buffer[24 * index];

        FILL_DMA_BUFFER(color.green);
        FILL_DMA_BUFFER(color.red);
        FILL_DMA_BUFFER(color.blue);

        spi_transmit_dma();
    }
}

void WS2812::clean(int index) { set_color(index, ws2812_color_t()); }

/*************************
 * HAL Related Functions *
 *************************/

void WS2812::init_spi_with_dma() {
    spi2_handle.Instance = SPI2;                                  /* SPI2 */
    spi2_handle.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2; /* 2分频 */
    spi2_handle.Init.Direction = SPI_DIRECTION_2LINES; /* 全双工 */
    spi2_handle.Init.CLKPolarity = SPI_POLARITY_LOW;   /* 时钟极性低 */
    spi2_handle.Init.CLKPhase =
        SPI_PHASE_2EDGE; /* 数据采样从第二个时钟边沿开始 */
    spi2_handle.Init.DataSize = SPI_DATASIZE_8BIT; /* SPI数据长度为8bit */
    spi2_handle.Init.FirstBit = SPI_FIRSTBIT_MSB;  /* 先发送MSB */
    spi2_handle.Init.NSS = SPI_NSS_HARD_OUTPUT; /* NSS软件模式(硬件模式) */
    spi2_handle.Init.Mode = SPI_MODE_MASTER;    /* 配置为主机 */

    if (HAL_SPI_DeInit(&spi2_handle) != HAL_OK) {
        Error_Handler();
    }

    if (HAL_SPI_Init(&spi2_handle) != HAL_OK) {
        Error_Handler();
    }
}

void WS2812::spi_transmit_dma() {
    uint8_t gap_buffer[WS2812_RESET_PULSE] = {};

    HAL_SPI_Transmit_DMA(&spi2_handle, dma_buffer, DMA_BUFFER_SIZE);
    while (HAL_SPI_GetState(&spi2_handle) != HAL_SPI_STATE_READY) {
        ;
    }
    HAL_SPI_Transmit_DMA(&spi2_handle, gap_buffer, WS2812_RESET_PULSE);
    while (HAL_SPI_GetState(&spi2_handle) != HAL_SPI_STATE_READY) {
        ;
    }
}

/* HAL_SPI_Init() 回调函数 */
void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi) {
    if (hspi->Instance == SPI2) {
        __HAL_RCC_DMA_CLK_ENABLE(); /* DMA时钟使能 */

        /*中断配置*/
        HAL_NVIC_SetPriority(SPI2_IRQn, 1, 0);
        HAL_NVIC_EnableIRQ(SPI2_IRQn);

        /*DMA_CH1配置*/
        hdma_tx.Instance = DMA1_Channel1;
        hdma_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
        hdma_tx.Init.PeriphInc = DMA_PINC_DISABLE;
        hdma_tx.Init.MemInc = DMA_MINC_ENABLE;

        if (hspi->Init.DataSize <= SPI_DATASIZE_8BIT) {
            hdma_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
            hdma_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
        } else {
            hdma_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
            hdma_tx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
        }

        hdma_tx.Init.Mode = DMA_NORMAL;
        hdma_tx.Init.Priority = DMA_PRIORITY_VERY_HIGH;

        /*DMA初始化*/
        HAL_DMA_Init(&hdma_tx);

        /*DMA句柄关联到SPI句柄*/
        __HAL_LINKDMA(hspi, hdmatx, hdma_tx);
        HAL_DMA_ChannelMap(&hdma_tx, DMA_CHANNEL_MAP_SPI2_TX);

        /*DMA中断设置*/
        HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 1, 0);
        HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
    }
}
